package com.gitee.wsl.jvm.task.thread

import androidx.lifecycle.LifecycleOwner
import com.gitee.wsl.ext.base.isTrue
import com.gitee.wsl.ext.coroutines.doOnLifecycle
import com.gitee.wsl.jvm.task.thread.TimeTaskServer.Companion.PROGRESS_TICK_INTERNAL
import com.gitee.wsl.jvm.task.thread.TimeTaskServer.Companion.PROGRESS_UPDATE_INITIAL_INTERVAL
import com.gitee.wsl.service.task.AbstractRetryAbleTask
import com.gitee.wsl.service.AbstractTaskService
import com.gitee.wsl.service.task.TaskStatus
import com.gitee.wsl.service.task.TimeTask
import com.gitee.wsl.service.task.scope.TimeTaskScope
import timber.log.Timber
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.ScheduledThreadPoolExecutor
import kotlin.time.DurationUnit
import kotlin.time.toTimeUnit



abstract class AbstractTimeTask(val mExecutorService: ScheduledExecutorService, var inMainLooper :Boolean= false): AbstractRetryAbleTask<TimeTaskScope, TaskStatus>(
    TaskStatus.UNKNOWN), TimeTask {
    protected var mScheduleFuture: ScheduledFuture<*>? = null
    protected var mProgressTask: Runnable? = null

    protected var initialLate : Long = 0L

    /**
     * 设置定时Runnable
     */
    fun setProgressTask(task: Runnable) {
        mProgressTask = task
    }

    /**
     * 停止更新进度条
     */
    protected fun stopToProgress() {
        isRunning().isTrue {
            mScheduleFuture?.cancel(false)
        }
    }

    /**
     * 释放资源
     */
    protected fun removeProgressTask() {
        stopToProgress()
    }

    /**
     * 是否在运行
     */
    override fun isRunning() = currentState() == TaskStatus.RUNNING


    override fun stop() {
        cancel()
    }

    override fun retry(): Int {
        return 0
    }

    override fun pause() {
        stopToProgress()
        setCurrentStatus(TaskStatus.PAUSED)
    }

    override fun late(lateTime: Long) {
        this.initialLate=lateTime
        start()
    }

    override fun reset() {
        late(0L)
    }

    override fun cancel() {
        removeProgressTask()
        setCurrentStatus(TaskStatus.FINISHED)
    }

}


class SingeRunTimeTaskImp( mExecutorService: ScheduledExecutorService, inMainLooper :Boolean= false):AbstractTimeTask(mExecutorService,inMainLooper){

    private var timeDelay : Long = PROGRESS_TICK_INTERNAL
    private var timeUnit:DurationUnit = DurationUnit.MILLISECONDS

    fun init(timeInternal: Long = PROGRESS_TICK_INTERNAL,timeUnit:DurationUnit = DurationUnit.MILLISECONDS){
        this.timeDelay=timeInternal
        this.timeUnit=timeUnit
    }

    /**
     * 开始更新
     */
    private fun startToProgress(timeDelay: Long, timeUnit:DurationUnit) {
        if (!mExecutorService.isShutdown) {
            mScheduleFuture = mExecutorService.schedule({
                setCurrentStatus(TaskStatus.RUNNING)
                Timber.d("run in startToUpdateProgress")
                mProgressTask?.run()
                setCurrentStatus(TaskStatus.FINISHED)
                fireCompleted(this)
            }, timeDelay, timeUnit.toTimeUnit())
        }
    }

    override fun start(){
        startToProgress(timeDelay,timeUnit)
    }

}


class TickTimeTaskImp(mExecutorService: ScheduledExecutorService, inMainLooper :Boolean= false): AbstractTimeTask(mExecutorService, inMainLooper){

    private var timeInternal : Long = PROGRESS_TICK_INTERNAL
    private var initial : Long= PROGRESS_UPDATE_INITIAL_INTERVAL

    fun init(timeInternal: Long = PROGRESS_TICK_INTERNAL, initial:Long= PROGRESS_UPDATE_INITIAL_INTERVAL){
        this.timeInternal=timeInternal
        this.initial=initial
    }

    /**
     * 开始更新
     * * 任务 时间间隔 一至
     */
    private fun startToProgress(timeInternal: Long = PROGRESS_TICK_INTERNAL, initial:Long= PROGRESS_UPDATE_INITIAL_INTERVAL) {
        stopToProgress()
        if (!mExecutorService.isShutdown) {
            mScheduleFuture = mExecutorService.scheduleAtFixedRate({
                setCurrentStatus(TaskStatus.RUNNING)
                mProgressTask?.run()
            }, initial,timeInternal,DurationUnit.MILLISECONDS.toTimeUnit())
        }
    }

    override fun start(){
        startToProgress(timeInternal,initial+initialLate)
    }
}

class StepTimeTaskImp(mExecutorService: ScheduledExecutorService, inMainLooper :Boolean= false): AbstractTimeTask(mExecutorService, inMainLooper){

    private var timeInternal : Long = PROGRESS_TICK_INTERNAL
    private var initial : Long= PROGRESS_UPDATE_INITIAL_INTERVAL

    fun init(timeInternal: Long = PROGRESS_TICK_INTERNAL, initial:Long= PROGRESS_UPDATE_INITIAL_INTERVAL){
        this.timeInternal=timeInternal
        this.initial=initial
    }

    /**
     * 开始更新
     * 任务 间隔时间 一至
     */
    private fun startToProgress(timeInternal: Long = PROGRESS_TICK_INTERNAL, initial:Long= PROGRESS_UPDATE_INITIAL_INTERVAL) {
        stopToProgress()
        if (!mExecutorService.isShutdown) {
            mScheduleFuture = mExecutorService.scheduleWithFixedDelay({
                setCurrentStatus(TaskStatus.RUNNING)
                mProgressTask?.run()
            }, initial,timeInternal,DurationUnit.MILLISECONDS.toTimeUnit())
        }
    }

    override fun start(){
        startToProgress(timeInternal,initial+initialLate)
    }
}

class TimeTaskServer: AbstractTaskService<TimeTask, TimeTaskScope, TaskStatus>() {

    companion object {
        const val PROGRESS_TICK_INTERNAL: Long = 1000
        const val PROGRESS_UPDATE_INITIAL_INTERVAL: Long = 100

        val timerTaskManager by lazy { TimeTaskServer() }

    }


    private val mExecutorService: ScheduledExecutorService by lazy {
        //Executors.newSingleThreadScheduledExecutor()
        ScheduledThreadPoolExecutor(5)
    }

    fun newSingeTimeTask():SingeRunTimeTaskImp{
        val task=SingeRunTimeTaskImp(mExecutorService)
        addNewTask(task)
        task.onCompleted {
            println("task completed:${task.tag}")
            removeTask(task)
        }
        return task
    }

    fun newTimeTask():TickTimeTaskImp{
        val task=TickTimeTaskImp(mExecutorService)
        addNewTask(task)
        task.onCompleted {
            println("task completed:${task.tag}")
            removeTask(task)
        }
        return task
    }

    fun newStepTimeTask():StepTimeTaskImp{
        val task=StepTimeTaskImp(mExecutorService)
        addNewTask(task)
        task.onCompleted {
            println("task completed:${task.tag}")
            removeTask(task)
        }
        return task
    }

    override fun stop() {
        super.stop()
        mExecutorService.shutdown()
    }
}


fun AbstractTimeTask.bindLifecycle(lifecycle: LifecycleOwner?) = apply {
    lifecycle?.doOnLifecycle(
        onDestroy = {
            stop()
        }
    )
}

fun TimeTaskServer.Companion.tickRun(lifecycleOwner: LifecycleOwner?=null, timeInternal: Long = TimeTaskServer.PROGRESS_TICK_INTERNAL, timeLate:Long= TimeTaskServer.PROGRESS_UPDATE_INITIAL_INTERVAL, taskRun: TimeTaskScope.()->Unit): TimeTask {
    val newTimeTask= TimeTaskServer.timerTaskManager.newTimeTask()
    newTimeTask.bindLifecycle(lifecycleOwner)
    newTimeTask.setProgressTask { taskRun(newTimeTask) }
    newTimeTask.init(timeInternal,timeLate)
    newTimeTask.start()
    return newTimeTask
}

fun TimeTaskServer.Companion.delayRun(timeDelay: Long = TimeTaskServer.PROGRESS_TICK_INTERNAL, timeUnit: DurationUnit = DurationUnit.MILLISECONDS, lifecycleOwner: LifecycleOwner?=null, taskRun:TimeTaskScope.()->Unit){
    val newTimeTask= TimeTaskServer.timerTaskManager.newSingeTimeTask()
    newTimeTask.bindLifecycle(lifecycleOwner)
    newTimeTask.setProgressTask { taskRun(newTimeTask) }
    newTimeTask.init(timeDelay,timeUnit)
    newTimeTask.start()
}

fun TimeTaskServer.Companion.stepRun(timeInternal: Long = TimeTaskServer.PROGRESS_TICK_INTERNAL, timeLate:Long= TimeTaskServer.PROGRESS_UPDATE_INITIAL_INTERVAL, lifecycleOwner: LifecycleOwner?=null, taskRun:TimeTaskScope.()->Unit){
    val newTimeTask= TimeTaskServer.timerTaskManager.newStepTimeTask()
    newTimeTask.bindLifecycle(lifecycleOwner)
    newTimeTask.setProgressTask { taskRun(newTimeTask) }
    newTimeTask.init(timeInternal,timeLate)
    newTimeTask.start()
}